"""
author: runyu.ma
"""

import random
from ..core.dslc_lexer import build_default_lexer
from ..core.dslc_parser import build_default_parser
from ..core.dslc_symbol_table import SymbolTable, Symbol
from ..core.dslc_parser import BinaryOp, BlockOp, ASTNode, Number, String, Identifier
from ..interpret import evaluation


def builtin_macro_pass(stab: SymbolTable, top: BinaryOp):
    if top.op != '!':
        raise ValueError("not CALL")
    __inner_call_dict = {
        'range_for': __replace_range_for,
        'global': __add_global_symtab,
        'rand_vec': __random_vector,
        'index_in': __index_in,
        'append': __append_block,
        'incfile': __include_file
    }
    if top.l.name in __inner_call_dict:
        return __inner_call_dict[top.l.name](stab, top.r)
    return top
    

def __replace_range_for(stab: SymbolTable, top: ASTNode):
    '''替换range_for'''
    if not isinstance(top, BlockOp):
        raise ValueError("not BlockOp")
    startval = evaluation(stab, top.oplist[0])
    stopval = evaluation(stab, top.oplist[1])
    stepval = evaluation(stab, top.oplist[2])
    oplist = []
    for i in range(eval(startval.value), eval(stopval.value), eval(stepval.value)):
        oplist.append(String(str(i)))
    return BlockOp(oplist)
    
def __add_global_symtab(stab: SymbolTable, top: ASTNode):
    '''设置一个全局变量'''
    if not isinstance(top, BlockOp):
        raise ValueError("not Enough param")
    if not isinstance(top.oplist[0], String):
        raise ValueError("first param should string")
    #处理name
    try:
        gloname = top.oplist[0].value.format_map(stab.lsvisiable())
    except:
        gloname = top.oplist[0].value
        print("add_global corrupt {}".format(top))
    name = "g::" + gloname
    # String 不会在evaluation的时候处理
    #if '{' in name and '}' in name:
    #    print("add_global corrupt {}", top)
    #    return top.parent
    #if name in stab.symbols:
    #    raise ValueError("already have {}".format(name))
    if isinstance(top.oplist[1], Identifier):
        isym = stab.lookup(top.oplist[1].name)
        if isym is None:
            print("add_global_symtab corrupt: skip {}".format(str(top.parent)))
            return top.parent
        if 'ival' in isym.info:
            value = isym.info['ival']            
            stab.symbols[name] = Symbol(name, {'ival': value})
            if isinstance(value, String):
                return BinaryOp('!', Identifier('global'), BlockOp([String(gloname), top.oplist[1]]))
                #return BinaryOp('!', Identifier('global'), BlockOp([String(gloname), String(value)]))
                #return BinaryOp('=', Identifier(top.oplist[0].value), String(value))
            return BinaryOp('!', Identifier('global'), BlockOp([String(gloname), top.oplist[1]]))
            #return BinaryOp('!', Identifier('global'), BlockOp([String(gloname), Number(value)]))
            #return BinaryOp('=', Identifier(top.oplist[0].value), Number(value))
        elif 'astobj' in isym.info:
            value = isym.info['astobj']
            #raise
            stab.symbols[name] = Symbol(name, {'astobj': value})
            return BinaryOp('!', Identifier('global'), BlockOp([String(gloname), top.oplist[1]]))
            #return BinaryOp('!', Identifier('global'), BlockOp([String(gloname), value]))
            #return BinaryOp('=', Identifier(top.oplist[0].value), value)
    else:
        #如果赋值是一个立即数
        _ival = top.oplist[1]
        if isinstance(_ival, BlockOp): 
            stab.symbols[name] = Symbol(name, {'astobj': _ival.copy()})
        else:
            stab.symbols[name] = Symbol(name, {'ival': _ival.value})
        if isinstance(_ival, String):
            return BinaryOp('!', Identifier('global'), BlockOp([String(gloname), String(top.oplist[1].value)]))
            #return BinaryOp('=', Identifier(top.oplist[0].value), String(top.oplist[1].value))
        elif isinstance(_ival, Number):
            return BinaryOp('!', Identifier('global'), BlockOp([String(gloname), Number(top.oplist[1].value)]))
            #return BinaryOp('=', Identifier(top.oplist[0].value), Number(top.oplist[1].value))
        else:
            #print('_ival', _ival)
            #print('-------------------LLLLLLLLLLLLL')
            return BinaryOp('!', Identifier('global'), BlockOp([String(gloname), _ival]))


def __random_vector(stab: SymbolTable, top: ASTNode):
    if isinstance(top.oplist[1], Number):
        btb = top.oplist[1].value
    elif isinstance(top.oplist[1], Identifier):
        isym = stab.lookup(top.oplist[1].name)
        if isym:
            btb = isym.info['ival']
        else:
            print(f"random_vector corrupt miss {top.oplist[1].name}")
            return top.parent
    if isinstance(top.oplist[2], Number):
        upb = top.oplist[2].value
    elif isinstance(top.oplist[2], Identifier):
        isym = stab.lookup(top.oplist[2].name)
        if isym:
            upb = isym.info['ival']
        else:
            print(f"random_vector corrupt miss {top.oplist[2].name}")
            return top.parent
    #print(stab, btb, upb)
    #raise
    if not isinstance(top, BlockOp):
        raise ValueError("not Enough param")
    if isinstance(top.oplist[0], Number):
        oplist = []
        count = top.oplist[0].value
        for i in range(count):
            rval = random.randint(int(btb), int(upb)-1)
            oplist.append(Number(str(rval)))
        return BlockOp(oplist)
    if isinstance(top.oplist[0], Identifier):
        oplist = []
        isym = stab.lookup(top.oplist[0].name)
        if isym:
            count = isym.info['ival']
        else:
            print("random_vector corrupt: skip {}".format(str(top.parent)))
            return top.parent
        oplist = []
        for i in range(count):
            rval = random.randint(int(btb), int(upb)-1)
            oplist.append(Number(str(rval)))
        return BlockOp(oplist)
    #
    raise ValueError("first param should number")


def __index_in(stab: SymbolTable, top: ASTNode):
    if isinstance(top.oplist[1], Number):
        fval = int(top.oplist[1].value)
    elif isinstance(top.oplist[1], Identifier):
        isym = stab.lookup(top.oplist[1].name)
        if isym:
            fval = isym.info['ival']
        else:
            print(f"random_vector corrupt miss {top.oplist[1].name}")
            return top.parent
    if not isinstance(top, BlockOp):
        raise ValueError("not Enough param")
    #
    if isinstance(top.oplist[0], Identifier):
        isym = stab.lookup(top.oplist[0].name)
        if isym is None:
            print("index_in corrupt: skip {}, name {}".format(str(top.parent), top.oplist[0].name))
            return top.parent
        #
        _oplist = isym.info['astobj'].oplist
        optgt = []
        for i in range(len(_oplist)):
            if int(_oplist[i].value) == fval:
                optgt.append(Number(f"{i}"))
            #optgt.append(Number(f"{i}"))
        #print(top, isym, _oplist, optgt)
        #print(optgt, _oplist, fval, type(fval))
        return BlockOp(optgt)
    #
    raise ValueError("first param should Block")


def __append_block(stab: SymbolTable, top: ASTNode):
    '''增加一个元素'''
    print(top)
    tgtname = top.oplist[0].name
    print(tgtname)
    tgtlist = stab.lookup(tgtname)
    if tgtlist and 'astobj' in tgtlist.info:
        tgtlist.info['astobj'].oplist.append(top.oplist[1])
        #raise
    return top.parent


def __include_file(stab: SymbolTable, top: ASTNode):
    with open(top.value) as file:
        cont = file.read()
        lexer = build_default_lexer()
        tokens = lexer.tokenize(cont)
        parser = build_default_parser(tokens)
        mast = parser.parse()
        return mast